//XXX
//#include <linux/unistd.h>
//#include <asm/ldt.h>
//#include <asm/unistd.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <linux/unistd.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <sys/uio.h>
#include <sys/termios.h>
#include <sys/utsname.h>
#include <sys/mman.h>
#include <sys/times.h>
#include <asm/ldt.h>


#include <sys/mman.h>

#include "crossbit/common.h"
#include "crossbit/Interpreter.h"
#include "crossbit/MemoryImage.h"
#include "crossbit/ELFMemoryImage.h"

#include "arch/ia32/IA32Syscall.h"
#include "arch/ia32/IA32ProcessorState.h"
#include "arch/ia32/IA32SyscallInternal.h"

using namespace crossbit;

#define EAX	0
#define ECX	1
#define EDX     2
#define EBX	3
#define ESP	4
#define EBP	5
#define ESI	6
#define EDI	7


IA32Syscall::IA32Syscall(IA32ProcessorState *state, MemoryImage *img)
    :SyscallHandler(state, img), state_(state), img_(img)
{
}

IA32Syscall::~IA32Syscall()
{
}

int IA32Syscall::syscode()
{
    return (int)(*(Word*)state_->get(0));
}

// Emulate IA32 syscall
void IA32Syscall::operator () ()
{
    /* address of the first element of register file */
    Word *r = (Word *)state_->get(0); 
    int syscode = (int)r[0];
    // LF("enter IA32Syscall, syscode = %d\n", % syscode);

    switch(syscode) {
	case IA32_SYS_exit:
	    // BRUTAL! FIXME
	    // LF("Emulating IA32trix Syscall: exit %d\n", % r[4]);
	    printf("exit\n");
	    exit(r[EBX]);
	    break;

	case IA32_SYS_fork:
	    {
		r[EAX] = fork();

		/*
		r[EAX] = 30427;
		*/
		break;
	    }
	case IA32_SYS_execve:
	    {
		char* filename = (char*)img_->map((MemAddr)r[EBX]);
		char** p = (char**)img_->map((MemAddr)r[ECX]);

		//execve(filename, p, environ);
		if (strcmp(filename, "/bin/sh") == 0)
		{
    		    char* arg0 = (char*)img_->map((MemAddr)p[2]);
    		    char buf[1024];
    
		    strcpy(buf, "/spec/20080708/IA32_FrontEnd/prog/ia322x86.nodebug ");
    		    strcat(buf, arg0);
    
		    char* cp[] = {"sh", "-c", buf, NULL};
    		    execve("/bin/sh", cp, environ); 
		}
		else
		{
		    int i = 0;
		    while(p[i] != 0)
 		    {
 			i ++;
 		    }

		    char** cp = new char*[i + 1];
		    cp[i] = NULL;
		    while (i > 0)
		    {
		        cp[i-1] = (char*)img_->map((MemAddr)p[i-1]);
		        i --;
		    }

		    execve("/bin/echo", cp, environ);
		}
		
		break;
	    }
	case IA32_SYS_pipe:
	    {
		int* filedes = (int*)img_->map(r[EBX]);
		r[EAX] = pipe(filedes);

		break;
	    }
	case IA32_SYS_dup2:
	    {
		//printf("dup2: %d, %d, %d\n", r[EBX], r[ECX], r[EDX]);
		r[EAX] = dup2(r[EBX], r[ECX]);
		break;
	    }
	case IA32_SYS_nanosleep:
	    {
		r[EAX] = nanosleep((struct timespec*)img_->map(r[EBX]), (struct timespec*)img_->map(r[ECX]));
		break;
	    }
	case IA32_SYS_wait4:
	    {
		int* status = (int*)img_->map((MemAddr)r[ECX]);
		struct rusage* rusage = (struct rusage*)img_->map(r[ESI]);

		r[EAX] = wait4(r[EBX], status, r[EDX], rusage);

		break;
	    }
	case IA32_SYS_read:
	    {
		r[EAX] = read((int)r[EBX], /*fd*/
			(char *)img_->map((MemAddr)r[ECX]), /*buf*/
			(int)r[EDX] /*nbyte*/);
		break;
	    }

	case IA32_SYS_write:
	    {
		// printf("************write  edx :%d***********\n", r[EDX]);
		r[EAX] = write((int)r[EBX], /*fd*/
			(char *)img_->map((MemAddr)r[ECX]), /*buf*/
			(int)r[EDX] /*nbyte*/);
		break;
	    }
	case IA32_SYS_uname:
		{
		r[EAX] = uname((utsname *)img_->map((MemAddr)r[EBX]));
		break;	
		}
	case IA32_SYS_unlink:
		{
		    r[EAX] = unlink((const char*)img_->map((MemAddr)r[EBX]));
		    break;
		}
   case IA32_SYS_gettimeofday:
      {
         struct timeval  *tv = (struct timeval *) img_->map (r[EBX]);
         struct timezone *tz = (struct timezone *)img_->map (r[ECX]);
		   r[EAX] = gettimeofday(tv, tz);
      }
	case IA32_SYS_rename:
		{
		    r[EAX] = rename((const char*)img_->map((MemAddr)r[EBX]),
			    (const char*)img_->map((MemAddr)r[ECX]));
		    break;
		}
	case IA32_SYS_mmap:
		{
		//XXX
		// printf("edx = %x, esi = %x, edi = %x\n", r[EDX], r[ESI], r[EDI]);
		int addr = *(int *)(r[ESP] + 4); 
		int len = *(int *)(r[ESP] + 8); 
		int prot = *(int *)(r[ESP] + 12); 
		int flags = *(int *)(r[ESP] + 16); 
		int fd = *(int *)(r[ESP] + 20); 
		int offset = *(int *)(r[ESP] + 24);
		r[EAX] = (XTInt32) SYSCALL::XTMmap((void *)(addr), (size_t)(len), (int)(prot), (int)(flags), (int)(fd), (off_t)(offset) ); 
                    break;
		}


	case IA32_SYS_munmap:
		{
		r[EAX] = SYSCALL::XTMunmap((void*)r[EBX], r[ECX]);
		break;
		}

	case IA32_SYS_mremap:
		{
		    char* src = (char*)img_->map(r[EBX]);

		    // r[EAX] = (unsigned int)((ELFMemoryImage*)img_)->mymap((void*)r[EBX], r[EDX], 0, 0, 0, -1);
		    char* dst = (char*)img_->map(r[EAX]);

		    for (int i = 0; i < r[ECX]; i ++)
		    {
			       dst[i] = src[i];
		    }
		    break;
		}

	case IA32_SYS_brk:
		{
		r[EAX] = (XTInt32)SYSCALL::XTBrk((void *)r[EBX]);
		// r[EAX] = (Word)img_->brk((MemAddr)r[EBX],0);
		//printf("brk, %08x\n", r[EBX]);
		//r[EAX] = brk((void*)r[EBX]);
		break;
		}

	case IA32_SYS_geteuid32:
		{
		r[EAX] = geteuid();
		break;
		}
	case IA32_SYS_getuid32:
		{
		r[EAX] = getuid();
		break;
		}
	case IA32_SYS_getegid32:
		{
		r[EAX] = getegid();
		break;
		}
	case IA32_SYS_getgid32:
		{
		r[EAX] = getgid();
		break;
		}

	case IA32_SYS_fstat64:
		{
		r[EAX] = fstat64((int)r[EBX], (struct stat64*)img_->map( (XTMemAddr)r[ECX]) );
		break;
		}

	case IA32_SYS_stat64:
		{
		    r[EAX] = stat64((char*)img_->map((MemAddr)r[EBX]), (struct stat64*)img_->map((MemAddr)r[ECX]));
		    break;
		}
	case IA32_SYS_fcntl64:
		{
		    r[EAX] = fcntl(r[EBX], r[ECX], r[EDX]);
		    break;
		}
	case IA32_SYS_getpid:
		{
		    r[EAX] = getpid();
		    break;
		}
	case IA32_SYS_exit_group:
		{
		r[EAX] = syscall(IA32_SYS_exit_group,((int)r[EBX]));
		break;
		}
    
	case IA32_SYS_open:
		{
		    r[EAX] =SYSCALL::XTOpen((const XTPStr)r[EBX], r[ECX], r[EDX]);
		    break;
		}
	case IA32_SYS_close:
		{
		    // printf("close fd: %d\n", r[EBX]);
		    r[EAX] = close(r[EBX]);
		    break;
		}

	// Added by Chuchao 6.3
	case IA32_SYS__llseek:
		{
		    int whence = r[EDI];
		    __off64_t offset = (unsigned long)r[ECX];
		    offset = offset << 32;
		    offset = offset | (unsigned long)r[EDX];
		    __off64_t result  = lseek64((int)r[EBX], offset, whence);
		    *(__off64_t *)img_->map(r[ESI]) = result;
		    r[EAX] = 0; 
		    break;
		}

	// Added by Chuchao 6.10
	case IA32_SYS_getrusage:
		{
		    int who = r[EBX];
		    r[EAX] = getrusage(who, (struct rusage *)img_->map((MemAddr)r[ECX]));		
		    break;
		}
	// Added by Chuchao 6.10
	case IA32_SYS_getrlimit:
		{
		    r[EAX] = getrlimit((int)r[EBX], (struct rlimit *)img_->map((MemAddr)r[ECX]));	
		    break;
		}

	// Added by Chuchao 6.10
	case IA32_SYS_ugetrlimit:
		{
		    r[EAX] = getrlimit((int)r[EBX], (struct rlimit *)img_->map((MemAddr)r[ECX]));	
		    break;
		}
	// Added by Chuchao 6.10
	case IA32_SYS_setrlimit:
		{
		    r[EAX] = setrlimit((int)r[EBX], (const struct rlimit *)img_->map((MemAddr)r[ECX]));	
		    break;
		}
	// Added by Chuchao 6.10
	case IA32_SYS_rt_sigprocmask:
		{
		    //dummy impl.
		    r[EAX]= 0;
		    break;	
		}

	case IA32_SYS_rt_sigaction:
		{
		    //dummy impl.
		    r[EAX]= 0;
		    break;
		}
	case IA32_SYS_ioctl:
		{
		    char buf[NUM_IOCTL_BYTES];

		    if (r[EDX] != 0)
		    {
			memcpy(buf, (void*)img_->map((MemAddr)r[EDX]), NUM_IOCTL_BYTES);	
		    }
		    r[EAX] = ioctl(r[EBX], r[ECX], buf);
		    //printf("ioctl\n");
		    break;
		}
	case IA32_SYS_readlink:
		{
		    r[EAX] = readlink((char*)img_->map((MemAddr)r[EBX]), (char*)img_->map((MemAddr)r[ECX]), (size_t)r[EDX]);
		    break;
		}
	case IA32_SYS_times:
		{
		    r[EAX] = times((struct tms*)img_->map((MemAddr)r[EBX]));
		    break;
		}
	case IA32_SYS_time:
		{
		    if (r[EBX] == 0)
		    {
			r[EAX] = time(NULL);
		    }
		    else
		    {
		        time_t * t = (time_t*)img_->map((MemAddr)r[EBX]);
		        r[EAX] = time(t);
		        //r[EAX] = time((time_t*)img_->map((MemAddr)r[EBX]));
		    }
		    break;
		}
	default:
		printf("Unknown syscall: 0x%x\n", syscode);
	    assert(0 && "Unrecognized syscall\n");
    }
}
